/* * Copyright (c) 2011 PonySDK * Owners: * Luciano Broussal <luciano.broussal AT gmail.com> * Mathieu Barbier <mathieu.barbier AT gmail.com> * Nicolas Ciaravola <nicolas.ciaravola.pro AT gmail.com> * * WebSite: * http://code.google.com/p/pony-sdk/ * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.ponysdk.core.terminal; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import com.google.gwt.dom.client.Element; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONValue; import com.google.gwt.user.client.History; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.UIObject; import com.google.gwt.user.client.ui.Widget; import com.ponysdk.core.model.ClientToServerModel; import com.ponysdk.core.model.HandlerModel; import com.ponysdk.core.model.ServerToClientModel; import com.ponysdk.core.model.WidgetType; import com.ponysdk.core.terminal.instruction.PTInstruction; import com.ponysdk.core.terminal.model.BinaryModel; import com.ponysdk.core.terminal.model.ReaderBuffer; import com.ponysdk.core.terminal.request.RequestBuilder; import com.ponysdk.core.terminal.ui.PTCookies; import com.ponysdk.core.terminal.ui.PTFrame; import com.ponysdk.core.terminal.ui.PTHistory; import com.ponysdk.core.terminal.ui.PTObject; import com.ponysdk.core.terminal.ui.PTStreamResource; import com.ponysdk.core.terminal.ui.PTWindow; import com.ponysdk.core.terminal.ui.PTWindowManager; import elemental.client.Browser; public class UIBuilder { private static final Logger log = Logger.getLogger(UIBuilder.class.getName()); private static final WidgetType[] WIDGET_TYPES = WidgetType.values(); private final UIFactory uiFactory = new UIFactory(); private final Map<Integer, PTObject> objectByID = new HashMap<>(); private final Map<UIObject, Integer> objectIDByWidget = new HashMap<>(); private final Map<Integer, UIObject> widgetIDByObjectID = new HashMap<>(); private final Map<String, JavascriptAddOnFactory> javascriptAddOnFactories = new HashMap<>(); private RequestBuilder requestBuilder; private static class AvoidBlockException extends Exception { } public void init(final RequestBuilder requestBuilder) { if (log.isLoggable(Level.INFO)) log.info("Init request builder"); this.requestBuilder = requestBuilder; PTHistory.addValueChangeHandler(this); final PTCookies cookies = new PTCookies(this); objectByID.put(0, cookies); // hide loading component final Widget w = RootPanel.get("loading"); if (w != null) { w.setSize("0px", "0px"); w.setVisible(false); } else { log.log(Level.WARNING, "Include splash screen html element into your index.html with id=\"loading\""); } } public void updateMainTerminal(final ReaderBuffer buffer) { while (buffer.hasRemaining()) { final int nextBlockPosition = buffer.shiftNextBlock(true); if (nextBlockPosition == -1) return; // Detect if the message is not for the main terminal but for a specific window final BinaryModel binaryModel = buffer.readBinaryModel(); final ServerToClientModel model = binaryModel.getModel(); if (ServerToClientModel.WINDOW_ID.equals(model)) { // Event on a specific window final int requestedId = binaryModel.getIntValue(); // Main terminal, we need to dispatch the eventbus final PTWindow window = PTWindowManager.getWindow(requestedId); if (window != null && window.isReady()) { if (log.isLoggable(Level.FINE)) log.fine("The main terminal send the buffer to window " + requestedId); window.postMessage(buffer.slice(buffer.getPosition(), nextBlockPosition)); } else { log.warning("The requested window " + requestedId + " doesn't exist anymore"); // TODO PERF LOG buffer.shiftNextBlock(false); } } else if (ServerToClientModel.FRAME_ID.equals(model)) { final int requestedId = binaryModel.getIntValue(); final PTFrame frame = (PTFrame) getPTObject(requestedId); if (log.isLoggable(Level.FINE)) log.fine("The main terminal send the buffer to frame " + requestedId); frame.postMessage(buffer.slice(buffer.getPosition(), nextBlockPosition)); } else if (ServerToClientModel.PING_SERVER.equals(model)) { if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Ping received"); final PTInstruction requestData = new PTInstruction(); requestData.put(ClientToServerModel.PING_SERVER, binaryModel.getLongValue()); requestBuilder.send(requestData); buffer.readBinaryModel(); // Read ServerToClientModel.END element } else if (ServerToClientModel.HEARTBEAT.equals(model)) { if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Heart beat received"); buffer.readBinaryModel(); // Read ServerToClientModel.END element } else if (ServerToClientModel.CREATE_CONTEXT.equals(model)) { PonySDK.get().setContextId(binaryModel.getIntValue()); buffer.readBinaryModel(); // Read ServerToClientModel.END element } else if (ServerToClientModel.DESTROY_CONTEXT.equals(model)) { destroy(); buffer.readBinaryModel(); // Read ServerToClientModel.END element } else { update(binaryModel, buffer); } } } public void updateWindowTerminal(final ReaderBuffer buffer) { // Detect if the message is not for the window but for a specific frame final BinaryModel binaryModel = buffer.readBinaryModel(); if (ServerToClientModel.FRAME_ID.equals(binaryModel.getModel())) { final int requestedId = binaryModel.getIntValue(); final PTFrame frame = (PTFrame) getPTObject(requestedId); if (log.isLoggable(Level.FINE)) log.fine("The main terminal send the buffer to frame " + requestedId); frame.postMessage(buffer.slice(buffer.getPosition(), buffer.shiftNextBlock(true))); } else { update(binaryModel, buffer); } } public void updateFrameTerminal(final ReaderBuffer buffer) { update(buffer.readBinaryModel(), buffer); } private void update(final BinaryModel binaryModel, final ReaderBuffer buffer) { final ServerToClientModel model = binaryModel.getModel(); final int modelOrdinal = model.ordinal(); try { if (ServerToClientModel.TYPE_CREATE.ordinal() == modelOrdinal) { final int objectID = binaryModel.getIntValue(); processCreate(buffer, objectID); processUpdate(buffer, objectID); } else if (ServerToClientModel.TYPE_UPDATE.ordinal() == modelOrdinal) { processUpdate(buffer, binaryModel.getIntValue()); } else if (ServerToClientModel.TYPE_ADD.ordinal() == modelOrdinal) { processAdd(buffer, binaryModel.getIntValue()); } else if (ServerToClientModel.TYPE_GC.ordinal() == modelOrdinal) { processGC(binaryModel.getIntValue()); } else if (ServerToClientModel.TYPE_REMOVE.ordinal() == modelOrdinal) { processRemove(buffer, binaryModel.getIntValue()); } else if (ServerToClientModel.TYPE_ADD_HANDLER.ordinal() == modelOrdinal) { processAddHandler(buffer, binaryModel.getIntValue()); } else if (ServerToClientModel.TYPE_REMOVE_HANDLER.ordinal() == modelOrdinal) { processRemoveHandler(buffer, binaryModel.getIntValue()); } else if (ServerToClientModel.TYPE_HISTORY.ordinal() == modelOrdinal) { processHistory(buffer, binaryModel.getStringValue()); } else { log.log(Level.WARNING, "Unknown instruction type : " + binaryModel + " ; " + buffer.toString()); buffer.shiftNextBlock(false); } buffer.readBinaryModel(); // Read ServerToClientModel.END element } catch (final AvoidBlockException e) { buffer.shiftNextBlock(false); } } private void processCreate(final ReaderBuffer buffer, final int objectID) throws AvoidBlockException { // ServerToClientModel.WIDGET_TYPE final WidgetType widgetType = WIDGET_TYPES[buffer.readBinaryModel().getByteValue()]; final PTObject ptObject = uiFactory.newUIObject(widgetType); if (ptObject != null) { ptObject.create(buffer, objectID, this); objectByID.put(objectID, ptObject); } else { log.warning("Cannot create PObject #" + objectID + " with widget type : " + widgetType); throw new AvoidBlockException(); } } private void processAdd(final ReaderBuffer buffer, final int objectID) throws AvoidBlockException { final PTObject ptObject = getPTObject(objectID); if (ptObject != null) { // ServerToClientModel.PARENT_OBJECT_ID final int parentId = buffer.readBinaryModel().getIntValue(); final PTObject parentObject = getPTObject(parentId); if (parentObject != null) { parentObject.add(buffer, ptObject); } else { log.warning("Cannot add " + ptObject + " to an garbaged parent object #" + parentId + ", so we will consume all the buffer of this object"); throw new AvoidBlockException(); } } else { log.warning("Add a null PTObject #" + objectID + ", so we will consume all the buffer of this object"); throw new AvoidBlockException(); } } private void processUpdate(final ReaderBuffer buffer, final int objectID) throws AvoidBlockException { final PTObject ptObject = getPTObject(objectID); if (ptObject != null) { BinaryModel binaryModel; boolean result = false; do { binaryModel = buffer.readBinaryModel(); if (binaryModel.getModel() != null) { result = !ServerToClientModel.END.equals(binaryModel.getModel()) ? ptObject.update(buffer, binaryModel) : false; } } while (result && buffer.hasRemaining()); if (!result) buffer.rewind(binaryModel); } else { log.warning("Update on a null PTObject #" + objectID + ", so we will consume all the buffer of this object"); throw new AvoidBlockException(); } } private void processRemove(final ReaderBuffer buffer, final int objectID) throws AvoidBlockException { final PTObject ptObject = getPTObject(objectID); if (ptObject != null) { final int parentId = buffer.readBinaryModel().getIntValue(); final PTObject parentObject = parentId != -1 ? getPTObject(parentId) : ptObject; if (parentObject != null) { parentObject.remove(buffer, ptObject); } else { log.warning("Cannot remove " + ptObject + " on a garbaged object #" + parentId); throw new AvoidBlockException(); } } else { log.warning("Remove a null PTObject #" + objectID + ", so we will consume all the buffer of this object"); throw new AvoidBlockException(); } } private void processAddHandler(final ReaderBuffer buffer, final int objectID) throws AvoidBlockException { final PTObject ptObject = getPTObject(objectID); // ServerToClientModel.HANDLER_TYPE final HandlerModel handlerModel = HandlerModel.values()[buffer.readBinaryModel().getByteValue()]; if (HandlerModel.HANDLER_STREAM_REQUEST.equals(handlerModel)) { new PTStreamResource().addHandler(buffer, handlerModel); } else if (ptObject != null) { ptObject.addHandler(buffer, handlerModel); } else { log.warning("Add handler on a null PTObject #" + objectID + ", so we will consume all the buffer of this object"); throw new AvoidBlockException(); } } private void processRemoveHandler(final ReaderBuffer buffer, final int objectID) throws AvoidBlockException { final PTObject ptObject = getPTObject(objectID); if (ptObject != null) { // ServerToClientModel.HANDLER_TYPE final HandlerModel handlerModel = HandlerModel.values()[buffer.readBinaryModel().getByteValue()]; ptObject.removeHandler(buffer, handlerModel); } else { log.warning("Remove handler on a null PTObject #" + objectID + ", so we will consume all the buffer of this object"); throw new AvoidBlockException(); } } private void processHistory(final ReaderBuffer buffer, final String token) { final String oldToken = History.getToken(); // ServerToClientModel.HISTORY_FIRE_EVENTS final boolean fireEvents = buffer.readBinaryModel().getBooleanValue(); if (oldToken != null && oldToken.equals(token)) { if (fireEvents) History.fireCurrentHistoryState(); } else { History.newItem(token, fireEvents); } } private void processGC(final int objectID) throws AvoidBlockException { final PTObject ptObject = unregisterObject(objectID); if (ptObject != null) { ptObject.destroy(); } else { log.warning("Cannot GC a garbaged PTObject #" + objectID); throw new AvoidBlockException(); } } private PTObject unregisterObject(final Integer objectID) { final PTObject ptObject = objectByID.remove(objectID); final UIObject uiObject = widgetIDByObjectID.remove(objectID); if (uiObject != null) objectIDByWidget.remove(uiObject); return ptObject; } private void destroy() { PTWindowManager.closeAll(); Browser.getWindow().getLocation().reload(); } public void sendDataToServer(final Widget widget, final PTInstruction instruction) { if (log.isLoggable(Level.FINE)) { if (widget != null) { final Element source = widget.getElement(); if (source != null) log.fine("Action triggered, Instruction [" + instruction + "] , " + source.getInnerHTML()); } } sendDataToServer(instruction); } public void sendDataToServer(final JSONValue instruction) { requestBuilder.send(instruction); } public void sendDataToServer(final JSONObject instruction) { final PTInstruction requestData = new PTInstruction(); final JSONArray jsonArray = new JSONArray(); jsonArray.set(0, instruction); requestData.put(ClientToServerModel.APPLICATION_INSTRUCTIONS, jsonArray); if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Data to send " + requestData.toString()); requestBuilder.send(requestData); } public void sendErrorMessageToServer(final String message) { final PTInstruction requestData = new PTInstruction(); requestData.put(ClientToServerModel.ERROR_MSG, message); requestBuilder.send(requestData); } public void sendInfoMessageToServer(final String message) { final PTInstruction requestData = new PTInstruction(); requestData.put(ClientToServerModel.INFO_MSG, message); requestBuilder.send(requestData); } public PTObject getPTObject(final Integer id) { final PTObject ptObject = objectByID.get(id); if (ptObject == null) log.warning("PTObject #" + id + " not found"); return ptObject; } public PTObject getPTObject(final UIObject uiObject) { final Integer objectID = objectIDByWidget.get(uiObject); if (objectID != null) return getPTObject(objectID); return null; } public void registerUIObject(final Integer ID, final UIObject uiObject) { objectIDByWidget.put(uiObject, ID); widgetIDByObjectID.put(ID, uiObject); } void registerJavascriptAddOnFactory(final String signature, final JavascriptAddOnFactory javascriptAddOnFactory) { this.javascriptAddOnFactories.put(signature, javascriptAddOnFactory); } public Map<String, JavascriptAddOnFactory> getJavascriptAddOnFactory() { return javascriptAddOnFactories; } void setReadyWindow(final int windowID) { final PTWindow window = PTWindowManager.getWindow(windowID); if (window != null) window.setReady(); else log.warning("Window " + windowID + " doesn't exist"); } void setReadyFrame(final int frameID) { final PTFrame frame = (PTFrame) getPTObject(frameID); if (frame != null) frame.setReady(); else log.warning("Frame " + frame + " doesn't exist"); } }